#ifndef SPECTRUM_FIXED_H
#define SPECTRUM_FIXED_H
#include <string>
#include <math.h>
#include <vector>
#include <sstream>
#include "common/simd/sse.h"
#include "spectrum_dynamic.h"
namespace gemren
{
#ifdef  _PLATFORM_WIN_64_ 
	template<int N>
	struct spectrum_fixed 
	{
		//const int N = 4;
		static const float infinity;
		
		typedef spectrum_fixed<N> spectrum;
	//	typedef spectrum_fixed spectrum;
		static spectrum pow(const spectrum& s, const float p)
		{
			spectrum ret;
			for(int i = 0; i < N; ++i)
				ret.vals_vec[i]= embree::ssef( 
					std::pow(s.vals_vec[i].v[0], p), 
					std::pow(s.vals_vec[i].v[1], p), 
					std::pow(s.vals_vec[i].v[2], p), 
					std::pow(s.vals_vec[i].v[3], p) 
					);
			return ret;
		}
		spectrum_fixed(const spectrum& s)
		{
			std::copy_n(s.vals_vec, N, vals_vec);
		}
		spectrum_fixed(const std::string& value, float min, float max)
		{
			spectrum_dynamic sd(value, min, max);
			copy_from_spectrum(sd);
		}
		~spectrum_fixed(){}
		spectrum_fixed& operator=(const spectrum& s)
		{
			std::copy_n(s.vals_vec, N, vals_vec);
			return *this;
		}
		template<typename Iter>
		spectrum_fixed(const Iter& begin, const Iter& end)
		{
			spectrum_dynamic sd(begin, end);
			copy_from_spectrum(sd);
		}
		spectrum_fixed()
		{
			init(0.0f);
		}
		spectrum_fixed(float val)
		{
			init(val);
		}
		float operator[](float wavelength) const
		{
			return at(wavelength);
		}
		inline float at(float wavelength) const
		{
			//assume 0 <= wl <= 1
			float * vals_f = (float*) vals_vec;
			const int len = 4*N-1;
			wavelength *= len;
			int left = int(wavelength), right = int(ceil(wavelength));
			float weight = fmod(wavelength, 1.0f);
			return (1.0f-weight)*vals_f[left] + weight*vals_f[right];
		}
		std::string to_string()
		{
			std::stringstream ss;
			for(int i = 0; i < 4*N; ++i)
				ss<<vals_vec[i/4].v[i%4]<<" ";
			return ss.str();
		}
		
		spectrum& operator *= (const float f)
		{
			for(int i = 0; i< N; ++i)
			{
				vals_vec[i] *= f;
			}
			return *this;
		}
		spectrum& operator *= (const spectrum& s)
		{
			for(int i = 0; i < N; ++i)
				vals_vec[i] *= s.vals_vec[i];
			return *this;
			
		}
		spectrum& operator += (const spectrum& s)
		{
			for(int i = 0; i < N; ++i)
				vals_vec[i] += s.vals_vec[i];
			return *this;
		}
		float max() const
		{
			float m = embree::reduce_max(vals_vec[0]).v[0];
			for(int i = 1; i < N; ++i)
			{
				float mc = embree::reduce_max(vals_vec[i]).v[0];
				if(mc > m) m = mc;
			}
			return m;
		}
		float min() const
		{
			float m = embree::reduce_min(vals_vec[0]).v[0];
			for(int i = 1; i < N; ++i)
			{
				float mc = embree::reduce_min(vals_vec[i]).v[0];
				if(mc < m) m = mc;
			}
			return m;
		}
		float integral() const
		{
			float ret = 0;
			for(int i = 0; i < N; ++i)
				ret += embree::reduce_add(vals_vec[i]).v[0];
			return ret/(4*N);
		}
		int getl() const { return 4*N-1; }
		float getv(int i) const { return vals_vec[i/4].v[i%4]; } 
		template<typename spec>
		void copy_from_spectrum(const spec& sd)
		{
			float * vals_f = (float*) vals_vec;
			for(int i = 0; i<4*N; ++i)
				vals_f[i] = sd[float(i)/(4*N)];
		/*	for(int i = 0; i<N; ++i)
				vals_vec[i] = embree::ssef( 
					sd[ float(4*i)/(4*N-1) ],
					sd[ float(4*i+1)/(4*N-1) ],
					sd[ float(4*i+2)/(4*N-1) ],
					sd[ float(4*i+3)/(4*N-1) ]);*/
		}
	protected:
		
		void init(float val)
		{
			float * vals_f = (float*) vals_vec;
			for(int i = 0; i< 4*N; ++i)
				vals_f[i] = val;
				//vals_vec[i] = embree::ssef(val, val, val, val); 
		}
		__declspec(align(16)) embree::ssef vals_vec[N];
	};

#else
//using normal floats instead of ssef to avoid problems
//it is however much slower
	template<int N>
	struct spectrum_fixed 
	{
		static const float infinity;
		
		typedef spectrum_fixed<N> spectrum;
		static spectrum pow(const spectrum& s, const float p)
		{
			spectrum ret;
			for(int i = 0; i < 4*N; ++i)
				ret.vals_f[i]= std::pow(s.vals_f[i], p);
			return ret;
		}
		spectrum_fixed(const spectrum& s)
		{
			std::copy_n(s.vals_f, 4*N, vals_f);
		}
		spectrum_fixed(const std::string& value, float min, float max)
		{
			spectrum_dynamic sd(value, min, max);
			copy_from_spectrum(sd);
		}
		~spectrum_fixed(){}
		spectrum_fixed& operator=(const spectrum& s)
		{
			std::copy_n(s.vals_f, 4*N, vals_f);
			return *this;
		}
		template<typename Iter>
		spectrum_fixed(const Iter& begin, const Iter& end)
		{
			spectrum_dynamic sd(begin, end);
			copy_from_spectrum(sd);
		}
		spectrum_fixed()
		{
			init(0.0f);
		}
		spectrum_fixed(float val)
		{
			init(val);
		}
		float operator[](float wavelength) const
		{
			return at(wavelength);
		}
		inline float at(float wavelength) const
		{
			//assume 0 <= wl <= 1
			const int len = 4*N-1;
			wavelength *= len;
			int left = int(wavelength), right = int(ceil(wavelength));
			float weight = fmod(wavelength, 1.0f);
			return (1.0f-weight)*vals_f[left] + weight*vals_f[right];
		}
		std::string to_string()
		{
			std::stringstream ss;
			for(int i = 0; i < 4*N; ++i)
				ss<<vals_f[i]<<" ";
			return ss.str();
		}
		
		spectrum& operator *= (const float f)
		{
			for(int i = 0; i< 4*N; ++i)
			{
				vals_f[i] *= f;
			}
			return *this;
		}
		spectrum& operator *= (const spectrum& s)
		{
			for(int i = 0; i < 4*N; ++i)
				vals_f[i] *= s.vals_f[i];
			return *this;
			
		}
		spectrum& operator += (const spectrum& s)
		{
			for(int i = 0; i < 4*N; ++i)
				vals_f[i] += s.vals_f[i];
			return *this;
		}
		float max() const
		{
			float m = vals_f[0];
			for(int i = 1; i < 4*N; ++i)
			{
				if(vals_f[i] > m) m = vals_f[i];
			}
			return m;
		}
		float min() const
		{
			float m = vals_f[0];
			for(int i = 1; i < 4*N; ++i)
			{
				if(vec_f[i] < m) m = vals_f[i];
			}
			return m;
		}
		float integral() const
		{
			float ret = 0;
			for(int i = 0; i < 4*N; ++i)
				ret += vals_f[i];
			return ret/(4*N);
		}
		int getl() const { return 4*N-1; }
		float getv(int i) const { return vals_f[i]; } 
		template<typename spec>
		void copy_from_spectrum(const spec& sd)
		{
			for(int i = 0; i<4*N; ++i)
				vals_f[i] = sd[float(i)/(4*N)];
		}
	protected:
		
		void init(float val)
		{
			for(int i = 0; i< 4*N; ++i)
				vals_f[i] = val; 
		}
		__declspec(align(16)) float vals_f[4*N];
	};

#endif

}

#endif